10分钟学会Go语言
Go(也称为Golang)是Google设计的一种静态类型、编译型编程语言。它以其简洁性、高效性和出色的并发支持而闻名。本教程将帮助你快速学习Go编程。
1. 编写第一个Go程序
让我们从一个简单的程序开始。创建一个名为hello.go
的文件并输入以下代码:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
保存文件并在终端中运行以下命令:
go run hello.go
输出将是:
Hello, World!
这个简单程序展示了Go的基本结构:
package main
声明包名import "fmt"
导入格式化包用于I/O操作func main()
是程序的入口点fmt.Println()
向控制台打印文本
2. 基本语法
Go具有简洁明了的语法。与Python不同,Go使用花括号{}
定义代码块,并且需要在语句末尾使用分号(虽然通常会自动插入)。
// 这是单行注释
fmt.Println("Hello, World!")
/*
这是多行注释
跨越多行
*/
Go中的基本语法规则:
- 代码块:由花括号
{}
定义 - 注释:单行注释以
//
开头,多行注释以/* */
包围 - 语句:以分号结束(自动插入)
- 包声明:每个文件都以包声明开始
3. 变量和数据类型
Go是静态类型语言,意味着你必须声明变量类型。但是,Go支持使用:=
操作符进行类型推断。
变量声明方法:
// 显式类型声明
var name string = "John"
var age int = 25
// 类型推断
name := "John"
age := 25
// 多变量声明
var x, y int = 10, 20
x, y := 10, 20
Go的主要基本数据类型:
- 整数类型:
int
,int8
,int16
,int32
,int64
,uint
,uint8
,uint16
,uint32
,uint64
,uintptr
- 浮点类型:
float32
,float64
- 字符串:
string
- 布尔值:
bool
- 复数类型:
complex64
,complex128
3.1 数值类型
Go为不同的使用场景提供了各种数值类型:
// 整数类型
var age int = 25
var smallNumber int8 = 127
var largeNumber int64 = 9223372036854775807
// 浮点类型
var temperature float32 = 36.5
var pi float64 = 3.14159265359
// 复数
var complexNum complex64 = 3 + 4i
3.2 字符串类型
Go中的字符串是字节序列并且是不可变的:
// 字符串声明
var greeting string = "Hello, Go!"
name := "Alice"
// 字符串操作
fmt.Println(len(greeting)) // 字符串长度
fmt.Println(greeting[0]) // 访问第一个字符(字节)
fmt.Println(greeting[0:5]) // 字符串切片
fmt.Println(strings.ToUpper(name)) // 转换为大写
3.3 布尔类型
布尔类型有两个值:true
和false
:
var isActive bool = true
var isComplete bool = false
// 布尔运算
result1 := true && false // false
result2 := true || false // true
result3 := !true // false
4. 常量
常量使用const
关键字声明且不能更改:
const Pi = 3.14159
const MaxUsers = 1000
// 多个常量
const (
StatusOK = 200
StatusNotFound = 404
StatusError = 500
)
// 类型化常量
const Version string = "1.0.0"
5. 数据结构
Go提供了几种内置数据结构来存储和操作数据。
5.1 数组
数组是相同类型元素的固定大小序列:
// 数组声明
var numbers [5]int = [5]int{1, 2, 3, 4, 5}
names := [3]string{"Alice", "Bob", "Charlie"}
// 访问元素
fmt.Println(numbers[0]) // 1
numbers[0] = 10 // 修改元素
// 数组长度
fmt.Println(len(numbers)) // 5
5.2 切片
切片是可以增长和缩小的动态数组:
// 切片声明
numbers := []int{1, 2, 3, 4, 5}
var emptySlice []int
// 从数组创建切片
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // [2, 3, 4]
// 切片操作
numbers = append(numbers, 6) // 添加元素
numbers = append(numbers, 7, 8, 9) // 添加多个元素
// 切片容量和长度
fmt.Println(len(numbers)) // 长度: 9
fmt.Println(cap(numbers)) // 容量: 10 (可能变化)
5.3 映射
映射是键值对的无序集合:
// 映射声明
student := map[string]interface{}{
"name": "John",
"age": 20,
"major": "Computer Science",
}
// 替代声明
var scores map[string]int = make(map[string]int)
scores["math"] = 95
scores["science"] = 88
// 访问和修改
fmt.Println(student["name"])
student["age"] = 21
student["gpa"] = 3.8
// 安全访问
if phone, exists := student["phone"]; exists {
fmt.Println(phone)
} else {
fmt.Println("未提供电话号码")
}
// 遍历映射
for key, value := range student {
fmt.Printf("%s: %v\n", key, value)
}
5.4 结构体
结构体是可以具有不同类型的字段集合:
// 结构体定义
type Person struct {
Name string
Age int
City string
}
// 创建结构体实例
person1 := Person{"Alice", 25, "New York"}
person2 := Person{
Name: "Bob",
Age: 30,
City: "London",
}
// 访问字段
fmt.Println(person1.Name)
person1.Age = 26
6. 运算和操作符
Go提供了丰富的操作符集用于各种计算和比较。
- 算术操作符:
+
,-
,*
,/
,%
(取模) - 比较操作符:
==
,!=
,>
,<
,>=
,<=
- 逻辑操作符:
&&
,||
,!
- 位操作符:
&
,|
,^
,<<
,>>
- 赋值操作符:
=
,+=
,-=
,*=
,/=
6.1 算术操作符
a, b := 10, 3
fmt.Printf("加法: %d\n", a + b) // 13
fmt.Printf("减法: %d\n", a - b) // 7
fmt.Printf("乘法: %d\n", a * b) // 30
fmt.Printf("除法: %d\n", a / b) // 3
fmt.Printf("取模: %d\n", a % b) // 1
6.2 比较操作符
x, y := 5, 10
fmt.Printf("相等: %t\n", x == y) // false
fmt.Printf("不相等: %t\n", x != y) // true
fmt.Printf("大于: %t\n", x > y) // false
fmt.Printf("小于: %t\n", x < y) // true
6.3 逻辑操作符
a, b := true, false
fmt.Printf("与运算: %t\n", a && b) // false
fmt.Printf("或运算: %t\n", a || b) // true
fmt.Printf("非运算: %t\n", !a) // false
7. 控制流
Go提供了几种控制流语句来管理程序执行。
7.1 if语句
age := 20
if age >= 18 {
fmt.Println("成年人")
} else if age >= 13 {
fmt.Println("青少年")
} else {
fmt.Println("儿童")
}
// 带有简短语句的if
if score := 85; score >= 90 {
fmt.Println("等级: A")
} else if score >= 80 {
fmt.Println("等级: B")
} else {
fmt.Println("等级: C")
}
7.2 for循环
Go只有一个循环结构:for
// 基本for循环
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// While风格循环
count := 0
for count < 5 {
fmt.Println(count)
count++
}
// 无限循环
for {
fmt.Println("这将永远运行")
break // 使用break退出
}
// Range循环(用于切片、数组、映射)
fruits := []string{"apple", "banana", "cherry"}
for index, fruit := range fruits {
fmt.Printf("%d: %s\n", index, fruit)
}
7.3 switch语句
day := "Monday"
switch day {
case "Monday":
fmt.Println("一周开始")
case "Friday":
fmt.Println("周末临近")
case "Saturday", "Sunday":
fmt.Println("周末!")
default:
fmt.Println("普通日子")
}
// 没有表达式的switch
score := 85
switch {
case score >= 90:
fmt.Println("等级: A")
case score >= 80:
fmt.Println("等级: B")
case score >= 70:
fmt.Println("等级: C")
default:
fmt.Println("等级: F")
}
8. 函数
Go中的函数是一等公民,支持多个返回值。
8.1 基本函数
func greet(name string) string {
return "Hello, " + name + "!"
}
// 调用函数
message := greet("John")
fmt.Println(message)
8.2 多个返回值
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("不能除以零")
}
return a / b, nil
}
// 使用多个返回值
result, err := divide(10, 2)
if err != nil {
fmt.Println("错误:", err)
} else {
fmt.Println("结果:", result)
}
8.3 命名返回值
func calculateRectangle(width, height float64) (area float64, perimeter float64) {
area = width * height
perimeter = 2 * (width + height)
return // 裸返回
}
area, perimeter := calculateRectangle(5, 3)
fmt.Printf("面积: %.2f, 周长: %.2f\n", area, perimeter)
8.4 可变参数函数
func sum(numbers ...int) int {
total := 0
for _, num := range numbers {
total += num
}
return total
}
fmt.Println(sum(1, 2, 3, 4)) // 10
fmt.Println(sum(5, 10, 15)) // 30
9. 指针
Go有指针,但语法比C/C++更简单:
func modifyValue(x *int) {
*x = *x * 2
}
func main() {
value := 10
fmt.Println("修改前:", value) // 10
modifyValue(&value)
fmt.Println("修改后:", value) // 20
}
10. 方法
方法是带有接收器参数的函数:
type Rectangle struct {
Width float64
Height float64
}
// 带有值接收器的方法
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// 带有指针接收器的方法
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
rect := Rectangle{Width: 5, Height: 3}
fmt.Println("面积:", rect.Area()) // 15
rect.Scale(2)
fmt.Println("缩放后面积:", rect.Area()) // 60
11. 接口
接口定义了类型可以实现的方法签名:
type Shape interface {
Area() float64
Perimeter() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * 3.14159 * c.Radius
}
func printShapeInfo(s Shape) {
fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}
circle := Circle{Radius: 5}
printShapeInfo(circle)
12. 错误处理
Go使用显式错误处理而不是异常:
func readFile(filename string) (string, error) {
data, err := os.ReadFile(filename)
if err != nil {
return "", fmt.Errorf("读取文件%s失败: %w", filename, err)
}
return string(data), nil
}
content, err := readFile("example.txt")
if err != nil {
fmt.Println("错误:", err)
return
}
fmt.Println("内容:", content)
13. 使用Goroutines的并发
Goroutines是由Go运行时管理的轻量级线程:
func worker(id int) {
for i := 0; i < 3; i++ {
fmt.Printf("工作者 %d: %d\n", id, i)
time.Sleep(time.Millisecond * 100)
}
}
func main() {
// 启动多个goroutines
for i := 1; i <= 3; i++ {
go worker(i)
}
// 等待goroutines完成
time.Sleep(time.Second)
fmt.Println("所有工作者已完成")
}
14. 通道
通道用于goroutines之间的通信:
func producer(ch chan<- int) {
for i := 0; i < 5; i++ {
ch <- i // 向通道发送值
time.Sleep(time.Millisecond * 100)
}
close(ch) // 完成后关闭通道
}
func consumer(ch <-chan int) {
for value := range ch {
fmt.Println("接收到:", value)
}
}
func main() {
ch := make(chan int, 3) // 缓冲通道
go producer(ch)
consumer(ch)
fmt.Println("通道通信完成")
}
15. 文件操作
Go提供了简单的方法来读写文件:
// 读取文件
data, err := os.ReadFile("example.txt")
if err != nil {
fmt.Println("读取文件错误:", err)
return
}
fmt.Println("文件内容:", string(data))
// 写入文件
content := "Hello, Go!\n"
err = os.WriteFile("output.txt", []byte(content), 0644)
if err != nil {
fmt.Println("写入文件错误:", err)
return
}
fmt.Println("文件写入成功")
16. 包和模块
Go模块管理依赖关系和包版本:
// 导入标准库包
import (
"fmt"
"math"
"strings"
)
func main() {
fmt.Println(math.Sqrt(16)) // 4
fmt.Println(strings.ToUpper("go")) // GO
}
要创建自己的包,请创建一个以包名命名的目录,并通过大写函数名来导出函数。
17. 测试
Go具有内置的测试支持:
// 在文件math_test.go中
package main
import "testing"
func TestAdd(t *testing.T) {
result := add(2, 3)
expected := 5
if result != expected {
t.Errorf("add(2, 3) = %d; 期望 %d", result, expected)
}
}
func add(a, b int) int {
return a + b
}
运行测试:go test
18. 最佳实践
- 使用
gofmt
格式化代码 - 遵循Go命名约定(变量使用camelCase,导出使用PascalCase)
- 显式处理错误
- 使用接口进行抽象
- 优先使用组合而不是继承
- 编写全面的测试
- 尽可能使用标准库
本教程涵盖了Go编程的基本特性。通过练习,你将能够使用Go的强大功能构建高效且并发的应用程序。